import { getComponent, getSlot } from "../_util/props-util"
import { PropType, defineComponent, ExtractPropTypes, Teleport, Transition, ref, toRef, watch, cloneVNode, onMounted, onUnmounted, TeleportProps, watchPostEffect } from "vue"
import Card from '../card'
import Button from '../button'
import Space from '../space'

const modalProps = {
  visible: Boolean,
  icon: Object,
  title: String,
  width: { default: 512 },
  bodyStyle: [String, Object],
  bodyClass: String,
  to: { type: [String, Object] as PropType<TeleportProps['to']>, default: 'body' },
  footer: Boolean,
  ok: { default: true },
  cancel: { default: true },
  mask: { default: true },
  maskClosable: { default: true },
  closable: { default: true },
  esc: { default: true },
  beforeOk: { type: Function as PropType<() => Boolean | Promise<void>> }
}

export type ModalProps = ExtractPropTypes<typeof modalProps>

export const prefixCls = 'x-modal'

export default defineComponent({
  name: prefixCls,
  inheritAttrs: false,
  props: modalProps,
  emits: ['update:visible', 'change', 'ok', 'cancel', 'afterLeave', 'maskClick'],
  setup(props, { emit, slots, attrs }) {

    const loadingRef = ref(false)
    
    const displayedRef = ref(props.visible)
    watch(toRef(props, 'visible'), val => {
      if (val) displayedRef.value = val
    })
    function onAfterLeave() {
      displayedRef.value = false
      emit('afterLeave')
    }

    const containerRef = ref<HTMLElement>()
    onMounted(() => {
      containerRef.value.addEventListener('scroll', e => {
        e.preventDefault()
        e.stopPropagation()
      })
    })

    // esc
    function onKeydown(e: KeyboardEvent) {
      if (e.key === 'Escape' && props.esc) close()
    }
    onMounted(() => {
      watchPostEffect(() => {
        if (props.esc && props.visible) document.addEventListener('keydown', onKeydown)
        else document.removeEventListener('keydown', onKeydown)
      })
    })
    onUnmounted(() => {
      document.removeEventListener('keydown', onKeydown)
    })

    function close() {
      emit('update:visible', false)
      emit('change', false)
    }
    function onMaskClick(e: MouseEvent) {
      console.log('onMaskClick');
      
      emit('maskClick', e)
      if (!props.maskClosable) return
      if (e.target === e.currentTarget) close()
    }
    function onOk() {
      if (props.beforeOk) {
        const ret: any = props.beforeOk()
        if (ret?.then) {
          loadingRef.value = true
          ret.then(() => {
            loadingRef.value = false
            close()
          })
        } else {
          if (ret !== false) close()
        }
      } else {
        close()
      }
      emit('ok')
    }
    function onCancel() {
      close()
      emit('cancel')
    }

    function getFooterNode() {
      if (slots.footer) {
        return slots.footer()
      } else if (props.footer) {
        const loading = loadingRef.value
        const cancelProps = { class: `${prefixCls}_cancel`, onClick: onCancel }
        const okProps = { class: `${prefixCls}_ok`, loading, autofocus: true, onClick: onOk }
        let cancelBtn = slots.cancel ? getSlot(slots.cancel)[0] : (props.cancel && <Button>取消</Button>);
        let okBtn = slots.ok ? getSlot(slots.ok)[0] : (props.ok && <Button type="primary">确认</Button>);
        cancelBtn = cancelBtn ? cloneVNode(cancelBtn, cancelProps) : undefined
        okBtn = okBtn ? cloneVNode(okBtn, okProps) : undefined
        return (
          <Space justify="end">
            {cancelBtn}
            {okBtn}
          </Space>
        )
      }
    }

    return vm => {
      const { to, visible, width, bodyStyle, bodyClass, closable } = props

      const cardProps = {
        ...attrs,
        class: prefixCls,
        style: { width: width + 'px' },
        closable,
        contentStyle: bodyStyle,
        contentClass: [`${prefixCls}_body`, bodyClass],
        footerClass: `${prefixCls}_footer`,
        role: 'document',
        onClose: onCancel
      }
      return (
        <Teleport to={to}>
          <div v-show={displayedRef.value} class={`${prefixCls}-container`} ref={containerRef}>
            <Transition name="fade" appear>
              <div v-show={props.visible} class={[`${prefixCls}-mask`, props.mask && 'show']} onClick={onMaskClick}></div>
            </Transition>
            <Transition name={prefixCls} appear onAfterLeave={onAfterLeave}>
              <Card v-show={visible} {...cardProps}>
                {{
                  icon: () => getComponent(vm, 'icon'),
                  title: () => getComponent(vm, 'title'),
                  default: slots.default,
                  footer: getFooterNode
                }}
              </Card>
            </Transition>
          </div>
        </Teleport>
      )
    }
  }
})